package com.krtech.wecard.module.sys.controller;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.dev33.satoken.SaManager;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.krtech.wecard.framework.exception.BusinessException;
import com.krtech.wecard.framework.model.CommRes;
import com.krtech.wecard.framework.security.Inner;
import com.krtech.wecard.framework.utils.LoginUtil;
import com.krtech.wecard.module.oauth2.vo.ClientSecretVo;
import com.krtech.wecard.module.pub.entity.ApplicationManagement;
import com.krtech.wecard.module.pub.entity.TnNotify;
import com.krtech.wecard.module.pub.service.ApplicationManagementService;
import com.krtech.wecard.module.pub.service.TnNotifyService;
import com.krtech.wecard.module.sys.entity.*;
import com.krtech.wecard.module.sys.enums.OrCodeDictEnums;
import com.krtech.wecard.module.sys.enums.SysEnums;
import com.krtech.wecard.module.sys.pojo.OrcodeFreezeListExport;
import com.krtech.wecard.module.sys.pojo.OrcodeLogExport;
import com.krtech.wecard.module.sys.pojo.OrcodeLogVo;
import com.krtech.wecard.module.sys.pojo.SysUserExport;
import com.krtech.wecard.module.sys.service.*;
import com.krtech.wecard.utils.DfsUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 二维码模块
 */
@RestController
@RequestMapping("/sys/orcode")
@Api(tags = "二维码模块")
@Slf4j
public class OrCodeController {

    @Autowired
    private SysKeyValueService keyValueService;

    @Autowired
    private ApplicationManagementService applicationManagementService;

    @Autowired
    private OrcodeLogService logService;

    @Autowired
    private OrcodeFreezeListService freezeListService;

    @Autowired
    private OrcodeConfigService configService;

    @Autowired
    private OrcodeDictService dictService;

    @Autowired
    private SysUserService userService;

    @Autowired
    private TnNotifyService notifyService;

    /**
     * 获得全局合法的client和secret
     *
     * @return
     */
    public List<ClientSecretVo> getClientAndSecretList(){
        List<ApplicationManagement> list = applicationManagementService.list();

        List<ClientSecretVo> applicationClientSecretList = new ArrayList<>();

        if (list.size() > 0){
            applicationClientSecretList = list.stream().map(applicationManagement -> {
                ClientSecretVo clientSecretVo = new ClientSecretVo();
                clientSecretVo.setClientId(applicationManagement.getAppKey());
                clientSecretVo.setSecretId(applicationManagement.getAppSecret());
                return clientSecretVo;
            }).collect(Collectors.toList());
        }


        SysKeyValue globalKeyAndSecret = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                .eq(SysKeyValue.COL_SYS_NAME, SysEnums.GLOBAL_KEY_AND_SECRET));

        if (globalKeyAndSecret != null){
            ClientSecretVo clientSecretVo = new ClientSecretVo();
            clientSecretVo.setClientId(globalKeyAndSecret.getSysKey());
            clientSecretVo.setSecretId(globalKeyAndSecret.getSysValue());
            applicationClientSecretList.add(clientSecretVo);
        }

        return applicationClientSecretList;
    }

    /**
     * 获得二维码地址
     */
    @GetMapping("/getOrCode")
    @ApiOperation(value = "获得二维码地址")
    public CommRes getOrCode(){
        SysKeyValue keyValue = keyValueService.getOne(new QueryWrapper<SysKeyValue>().eq(SysKeyValue.COL_SYS_KEY, SysEnums.OR_CODE_ADDRESS));
        if (keyValue == null){
            throw new BusinessException("二维码前缀地址未配置，请配置");
        }
        String orCode = keyValue.getSysValue() + IdUtil.simpleUUID();

        SysKeyValue overdueTime = keyValueService.getOne(new QueryWrapper<SysKeyValue>().eq(SysKeyValue.COL_SYS_KEY, SysEnums.OR_CODE_OVERDUE_TIME));
        if (overdueTime == null){
            throw new BusinessException("二维码过期时间未配置，请配置");
        }

        SaManager.getSaTokenDao().set(orCode, JSONUtil.toJsonStr(LoginUtil.getUserInfo()),Long.parseLong(overdueTime.getSysValue()));

        return CommRes.ok(orCode);
    }

    /**
     * 校验二维码地址
     */
    @GetMapping("/judgeOrCode")
    @ApiOperation(value = "校验二维码地址")
    public CommRes judgeOrCode(String orCode){
        String orCodeObj = SaManager.getSaTokenDao().get(orCode);
        if (StrUtil.isEmpty(orCodeObj)){
            return CommRes.error("10004","该二维码已失效或不存在");
        }

        return CommRes.ok();
    }


    /**
     * 开放给第三方二维码验证
     */
    @PostMapping("/orCodeApply")
    @ApiOperation(value = "开放给第三方二维码验证")
    @Inner(value = false)
    public CommRes orCodeApply(@RequestBody OrCodeBean orCodeBean){
        //必填参数校验

        String orCodeObj = SaManager.getSaTokenDao().get(orCodeBean.getAuth_code());
        if (StrUtil.isEmpty(orCodeObj)){
            return CommRes.error("10004","该二维码地址已失效或不存在");
        }

        //根据key获得secret
        List<ClientSecretVo> clientAndSecretList = getClientAndSecretList();
        List<ClientSecretVo> secretVoList = clientAndSecretList.stream().filter(clientSecretVo -> clientSecretVo.getClientId().equals(orCodeBean.getApp_key())).collect(Collectors.toList());
        //取得key，然后用key在后台得到secret，进行加密，得到signature
        if (secretVoList.size() <= 0){
            return CommRes.error("1005","app_key错误");
        }
        String sign = sign(orCodeBean, secretVoList.get(0).getSecretId());
        sign = StrUtil.swapCase(sign);
        if (!sign.equals(orCodeBean.getSignature())){
            return CommRes.error("1002","签名验证失败，请使用签名验证工具校验");
        }

        //返回用户信息
        SysUser user = JSONUtil.toBean(orCodeObj, SysUser.class);
        //封装成对应的实体
        OrCodeResUser orCodeResUser = new OrCodeResUser();
        orCodeResUser.setCardNumber(user.getUserCode());

        return CommRes.ok(orCodeResUser);
    }

    /**
     * 签名方法sign
     *      忽略掉signature
     */
    public static String sign(OrCodeBean orCodeBean,String secret){
        Map<String, Object> map = BeanUtil.beanToMap(orCodeBean);
        map.remove("signature");
        List<String> keySortList = map.keySet().stream().sorted().collect(Collectors.toList());



        String signStr = "";
        for (String keySort:keySortList){
            Object obj = map.get(keySort);
            if (obj != null && obj != ""){
                signStr = signStr + keySort + "=" + obj + "&";
            }
        }

        signStr = signStr + "key=" +secret ;

        String sign = DigestUtil.md5Hex(signStr);
        return sign;
    }

    /**
     * 二维码截图风控 新增
     */
    @PutMapping("/add")
    @ApiOperation(value = "新增")
    public OrcodeConfig add(@RequestBody OrcodeConfig config){
        //查询现在的规则是否有相同次数了
        int count = configService.count(new QueryWrapper<OrcodeConfig>().eq(OrcodeConfig.COL_LATEST_YEAR_SCREENSHOTS, config.getLatestYearScreenshots()));
        if (count > 0){
            throw new BusinessException("不增加重复触发次数");
        }
        configService.save(config);
        return config;

    }

    /**
     * 二维码截图风控 更改
     */
    @PostMapping("/update")
    @ApiOperation(value = "更改")
    public OrcodeConfig update(@RequestBody OrcodeConfig config){
        //查询现在的规则是否有相同次数了
        if (config.getId() == null){
            throw new BusinessException("必填参数id不能为空");
        }
        configService.updateById(config);
        return config;
    }

    /**
     * 日志
     */
    @GetMapping("/log")
    @ApiOperation(value = "日志")
    public OrcodeLogVo log(){
        SysUser user = LoginUtil.getUserInfo();
        Date date = new Date();
        OrcodeLog orcodeLog = new OrcodeLog();
        orcodeLog.setCreatedTime(date);
        orcodeLog.setScreenshotsTime(date);
        orcodeLog.setUpdatedTime(date);
        orcodeLog.setCreatedBy(user.getUserCode());
        orcodeLog.setUpdatedBy(user.getUserCode());

        orcodeLog.setName(user.getNickName());
        orcodeLog.setCode(user.getUserCode());

        logService.save(orcodeLog);

        //查询该用户近一年的记录

        int screenshotsCount = logService.count(new QueryWrapper<OrcodeLog>().eq(OrcodeLog.COL_CODE,user.getUserCode())
                .between(OrcodeLog.COL_SCREENSHOTS_TIME, DateUtil.offsetDay(date, -365), DateUtil.formatDateTime(date)).eq(OrcodeLog.COL_DEL_FLAG,0));
        //将次数+1
        screenshotsCount = screenshotsCount + 1;

        OrcodeConfig config = configService.getOne(new QueryWrapper<OrcodeConfig>().eq(OrcodeConfig.COL_LATEST_YEAR_SCREENSHOTS, screenshotsCount));

        if (config != null){
            // 根据规则风控
            String dictCode = config.getCode();
            OrcodeDict dict = dictService.getOne(new QueryWrapper<OrcodeDict>().eq(OrcodeDict.COL_CODE, dictCode));
            if (dict.getCode().equals(OrCodeDictEnums.WARN)){
                // 发送消息进行警告
                TnNotify tnNotify = new TnNotify();
                tnNotify.setNotifyContent("触发风控，近一年截图二维码达到"+screenshotsCount+"次,请不要对二维码进行截图，避免出现安全问题");
                tnNotify.setNotifyFallPaper("二维码截屏风控中心");
                tnNotify.setNotifyAbstract("二维码请勿截屏");
                tnNotify.setNotifyTitle("二维码请勿截屏");
                tnNotify.setNotifyTargetTypeName("消息警告");
                ArrayList<SysUser> userArrayList = new ArrayList<>();
                userArrayList.add(user);
                String userList = JSONUtil.toJsonStr(userArrayList);
                tnNotify.setUserList(userList);
                notifyService.save(tnNotify);

                orcodeLog.setTriggerFunction("触发风控，近一年截图二维码达到"+screenshotsCount+"次,被实施"+dict.getName()+"策略");
                orcodeLog.setTriggerFunctionCode(dict.getCode());

            }else if (dict.getCode().contains(OrCodeDictEnums.FREEZE)){
                //包含了freeze就是冻结级别，根据时间长短进行冻结
                //判断当前冻结名单是否有这个人，有的话就是更新冻结和解冻时间
                OrcodeFreezeList nowFreeze = freezeListService.getOne(new QueryWrapper<OrcodeFreezeList>().eq(OrcodeFreezeList.COL_CODE, user.getUserCode()));

                //计算解冻时间
                DateTime unFreezeTime = DateUtil.offsetMinute(date, Integer.parseInt(String.valueOf(dict.getTimeLong())));


                if (nowFreeze == null){
                    OrcodeFreezeList freezeList = new OrcodeFreezeList();
                    freezeList.setCreatedTime(date);
                    // 手动指定解冻时间
                    freezeList.setFreezeTime(date);
                    freezeList.setUnfreezeTime(unFreezeTime);


                    freezeList.setUpdatedTime(date);
                    freezeList.setCreatedBy(user.getUserCode());
                    freezeList.setUpdatedBy(user.getUserCode());

                    freezeList.setName(user.getNickName());
                    freezeList.setCode(user.getUserCode());

                    freezeList.setFreezeReason("触发风控，近一年截图二维码达到"+screenshotsCount+"次");
                    freezeList.setFreezeFunction("触发风控冻结");

                    orcodeLog.setTriggerFunction("触发风控，近一年截图二维码达到"+screenshotsCount+"次,被实施"+dict.getName()+"策略");
                    orcodeLog.setTriggerFunctionCode(dict.getCode());

                    freezeListService.save(freezeList);
                }else {
                    nowFreeze.setFreezeTime(date);
                    nowFreeze.setUnfreezeTime(unFreezeTime);

                    nowFreeze.setUpdatedBy(user.getUserCode());
                    nowFreeze.setUpdatedTime(date);

                    nowFreeze.setFreezeReason("触发风控，近一年截图二维码达到"+screenshotsCount+"次");
                    nowFreeze.setFreezeFunction("触发风控冻结");


                    orcodeLog.setTriggerFunction("触发风控，近一年截图二维码达到"+screenshotsCount+"次,被实施"+dict.getName()+"策略");
                    orcodeLog.setTriggerFunctionCode(dict.getCode());
                    freezeListService.updateById(nowFreeze);
                }

            }
        }

        OrcodeLogVo orcodeLogVo = new OrcodeLogVo();
        BeanUtil.copyProperties(orcodeLog,orcodeLogVo);
        orcodeLogVo.setScreenshotsCount(screenshotsCount);
        return orcodeLogVo;
    }


    @GetMapping("/page")
    @ApiOperation(value = "截屏记录分页查询")
    public Page<OrcodeLogVo> page(Page<OrcodeLog> page, String keyword){
        QueryWrapper<OrcodeLog> wrapper = new QueryWrapper<>();
        if (StrUtil.isNotEmpty(keyword)){
            wrapper.like(OrcodeLog.COL_NAME,keyword)
                    .or()
                    .like(OrcodeLog.COL_CODE,keyword);
        }
        wrapper.orderByDesc(OrcodeLog.COL_SCREENSHOTS_TIME);

        Page<OrcodeLog> managementPage = logService.page(page, wrapper);

        Date date = new Date();

        List<OrcodeLogVo> orcodeLogVoList = managementPage.getRecords().stream().map(orcodeLog -> {
            OrcodeLogVo orcodeLogVo = new OrcodeLogVo();
            BeanUtil.copyProperties(orcodeLog, orcodeLogVo);

            //查询冻结名单是否有这个人
            OrcodeFreezeList nowFreeze = freezeListService.getOne(new QueryWrapper<OrcodeFreezeList>().eq(OrcodeFreezeList.COL_CODE, orcodeLogVo.getCode()));
            if (nowFreeze == null) {
                orcodeLogVo.setHasFreeze(0);
            } else {
                //判读啊冻结时间
                if (DateUtil.compare(date, nowFreeze.getUnfreezeTime()) > 0) {
                    //解冻
                    orcodeLogVo.setHasFreeze(0);
                } else {
                    orcodeLogVo.setHasFreeze(1);
                }
            }
            return orcodeLogVo;
        }).collect(Collectors.toList());

        Page<OrcodeLogVo> voPage = new Page<>();
        BeanUtil.copyProperties(managementPage,voPage);
        voPage.setRecords(orcodeLogVoList);

        return voPage;
    }

    @GetMapping("/screenshotsExport")
    @ApiOperation(value = "截屏记录导出")
    public DfsUtil.DfsFile screenshotsExport(HttpServletResponse response, String keyword){


            QueryWrapper<OrcodeLog> wrapper = new QueryWrapper<>();
            if (StrUtil.isNotEmpty(keyword)){
                wrapper.like(OrcodeLog.COL_NAME,keyword)
                        .or()
                        .like(OrcodeLog.COL_CODE,keyword);
            }
            wrapper.orderByDesc(OrcodeLog.COL_SCREENSHOTS_TIME);

            List<OrcodeLog> orcodeLogs = logService.list(wrapper);

            Date date = new Date();

            List<OrcodeLogExport> orcodeLogVoList = orcodeLogs.stream().map(orcodeLog -> {
                OrcodeLogExport orcodeLogVo = new OrcodeLogExport();
                BeanUtil.copyProperties(orcodeLog, orcodeLogVo);

                //查询冻结名单是否有这个人
                OrcodeFreezeList nowFreeze = freezeListService.getOne(new QueryWrapper<OrcodeFreezeList>().eq(OrcodeFreezeList.COL_CODE, orcodeLogVo.getCode()));
                if (nowFreeze == null) {
                    orcodeLogVo.setHasFreeze(0);
                } else {
                    //判读啊冻结时间
                    if (DateUtil.compare(date, nowFreeze.getUnfreezeTime()) > 0) {
                        //解冻
                        orcodeLogVo.setHasFreeze(0);
                    } else {
                        orcodeLogVo.setHasFreeze(1);
                    }
                }
                return orcodeLogVo;
            }).collect(Collectors.toList());


        DfsUtil dfsUtil = new DfsUtil();
        try {
            File file = FileUtil.createTempFile("截屏记录", ".xls", new File("./"), true);
            Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("截屏记录","截屏记录"), OrcodeLogExport.class, orcodeLogVoList);
            BufferedOutputStream outputStream = FileUtil.getOutputStream(file);
            workbook.write(outputStream);
            outputStream.flush();
            outputStream.close();
            DfsUtil.DfsFile upload = dfsUtil.upload(file);
            return upload;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        throw new BusinessException("导出异常");
    }

    @GetMapping("/freezePage")
    @ApiOperation(value = "截图冻结分页查询")
    public Page<OrcodeFreezeList> freezePage(Page<OrcodeFreezeList> page, String keyword){
        QueryWrapper<OrcodeFreezeList> wrapper = new QueryWrapper<>();
        if (StrUtil.isNotEmpty(keyword)){
            wrapper.like(OrcodeFreezeList.COL_NAME,keyword)
                    .or()
                    .like(OrcodeFreezeList.COL_CODE,keyword);
        }
        wrapper.ge(OrcodeFreezeList.COL_UNFREEZE_TIME,DateUtil.now());
        Page<OrcodeFreezeList> managementPage = freezeListService.page(page, wrapper);
        return managementPage;
    }

    @GetMapping("/freezeExport")
    @ApiOperation(value = "冻结名单导出")
    public DfsUtil.DfsFile freezeExport(HttpServletResponse response, String keyword){


        QueryWrapper<OrcodeFreezeList> wrapper = new QueryWrapper<>();
        if (StrUtil.isNotEmpty(keyword)){
            wrapper.like(OrcodeFreezeList.COL_NAME,keyword)
                    .or()
                    .like(OrcodeFreezeList.COL_CODE,keyword);
        }
        wrapper.ge(OrcodeFreezeList.COL_UNFREEZE_TIME,DateUtil.now());
        List<OrcodeFreezeList> freezeListList = freezeListService.list(wrapper);

        List<OrcodeFreezeListExport> collect = freezeListList.stream().map(freezeList -> {
            OrcodeFreezeListExport orcodeFreezeListExport = new OrcodeFreezeListExport();
            BeanUtil.copyProperties(freezeList, orcodeFreezeListExport);
            return orcodeFreezeListExport;
        }).collect(Collectors.toList());

        DfsUtil dfsUtil = new DfsUtil();
        try {
            File file = FileUtil.createTempFile("冻结名单", ".xls", new File("./"), true);
            Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("冻结名单","冻结名单"), OrcodeFreezeListExport.class, collect);
            BufferedOutputStream outputStream = FileUtil.getOutputStream(file);
            workbook.write(outputStream);
            outputStream.flush();
            outputStream.close();
            DfsUtil.DfsFile upload = dfsUtil.upload(file);
            return upload;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        throw new BusinessException("导出异常");
    }

    @GetMapping("/configList")
    @ApiOperation(value = "策略配置list")
    public List<OrcodeConfig> page(){
        QueryWrapper<OrcodeConfig> wrapper = new QueryWrapper<>();

        List<OrcodeConfig> list = configService.list(wrapper);
        return list;
    }

    @GetMapping("/dictList")
    @ApiOperation(value = "字典配置list")
    public List<OrcodeDict> dictList(){
        QueryWrapper<OrcodeDict> wrapper = new QueryWrapper<>();

        List<OrcodeDict> list = dictService.list(wrapper);
        return list;
    }

    /**
     * 冻结方式传code
     *
     * 解冻时间永久考虑传入一个1000年以后的时间
     *
     * @param freezeList
     * @return
     */
    @PostMapping("/freeze")
    @ApiOperation(value = "冻结")
    public OrcodeFreezeList freeze(@RequestBody OrcodeFreezeList freezeList){
        SysUser userInfo = LoginUtil.getUserInfo();
        SysUser user = userService.getOne(new QueryWrapper<SysUser>().eq(SysUser.COL_USER_CODE, freezeList.getCode()));

        //判断当前冻结名单是否有这个人，有的话就是更新冻结和解冻时间
        Date date = new Date();

        OrcodeFreezeList nowFreeze = freezeListService.getOne(new QueryWrapper<OrcodeFreezeList>().eq(OrcodeFreezeList.COL_CODE, freezeList.getCode()));
        if (nowFreeze == null){
            freezeList.setCreatedTime(date);
            // 手动指定解冻时间
            freezeList.setFreezeTime(date);
            freezeList.setUpdatedTime(date);
            freezeList.setCreatedBy(userInfo.getUserCode());
            freezeList.setUpdatedBy(userInfo.getUserCode());

            freezeList.setName(user.getNickName());
            freezeList.setCode(user.getUserCode());

            freezeListService.save(freezeList);
        }else {
            nowFreeze.setFreezeTime(date);
            nowFreeze.setUnfreezeTime(freezeList.getUnfreezeTime());
            nowFreeze.setUpdatedBy(userInfo.getUserCode());
            nowFreeze.setUpdatedTime(date);
            freezeListService.updateById(nowFreeze);
        }




        return freezeList;
    }

    /**
     * 冻结方式传code
     *
     * @return
     */
    @GetMapping("/unfreeze")
    @ApiOperation(value = "解冻")
    public boolean unfreeze(String code){
        return logService.unfreeze(code);
    }


}
